package org.richin.lang.util;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

public abstract class NumberUtils extends org.apache.commons.lang.math.NumberUtils {

	public NumberUtils() {
		super();
	}

	/**
	 * Convert the given number into an instance of the given target class.
	 * @param number the number to convert
	 * @param targetClass the target class to convert to
	 * @return the converted number
	 * @throws IllegalArgumentException if the target class is not supported
	 * (i.e. not a standard Number subclass as included in the JDK)
	 * @see java.lang.Byte
	 * @see java.lang.Short
	 * @see java.lang.Integer
	 * @see java.lang.Long
	 * @see java.math.BigInteger
	 * @see java.lang.Float
	 * @see java.lang.Double
	 * @see java.math.BigDecimal
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Number> T convertNumberToTarget(Number number, Class<T> targetClass)
			throws IllegalArgumentException {

		Assert.notNull(number, "Number must not be null");
		Assert.notNull(targetClass, "Target class must not be null");

		if (targetClass.isInstance(number)) {
			return (T) number;
		} else if (targetClass.equals(Byte.class)) {
			long value = number.longValue();
			if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
				raiseOverflowException(number, targetClass);
			}
			return (T) new Byte(number.byteValue());
		} else if (targetClass.equals(Short.class)) {
			long value = number.longValue();
			if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
				raiseOverflowException(number, targetClass);
			}
			return (T) new Short(number.shortValue());
		} else if (targetClass.equals(Integer.class)) {
			long value = number.longValue();
			if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
				raiseOverflowException(number, targetClass);
			}
			return (T) new Integer(number.intValue());
		} else if (targetClass.equals(Long.class)) {
			return (T) new Long(number.longValue());
		} else if (targetClass.equals(BigInteger.class)) {
			if (number instanceof BigDecimal) {
				// do not lose precision - use BigDecimal's own conversion
				return (T) ((BigDecimal) number).toBigInteger();
			} else {
				// original value is not a Big* number - use standard long conversion
				return (T) BigInteger.valueOf(number.longValue());
			}
		} else if (targetClass.equals(Float.class)) {
			return (T) new Float(number.floatValue());
		} else if (targetClass.equals(Double.class)) {
			return (T) new Double(number.doubleValue());
		} else if (targetClass.equals(BigDecimal.class)) {
			// always use BigDecimal(String) here to avoid unpredictability of BigDecimal(double)
			// (see BigDecimal javadoc for details)
			return (T) new BigDecimal(number.toString());
		} else {
			throw new IllegalArgumentException("Could not convert number [" + number + "] of type ["
					+ number.getClass().getName() + "] to unknown target class [" + targetClass.getName() + "]");
		}
	}

	public static Number convertNumberToTargetClass(Number number, Class targetClass) throws IllegalArgumentException {
		if(number==null)
			return 0;
		Assert.notNull(targetClass, "Target class must not be null");
		if (targetClass.isInstance(number))
			return number;
		if (targetClass.equals(java.lang.Byte.class)) {
			long value = number.longValue();
			if (value < -128L || value > 127L)
				raiseOverflowException(number, targetClass);
			return new Byte(number.byteValue());
		}
		if (targetClass.equals(java.lang.Short.class)) {
			long value = number.longValue();
			if (value < -32768L || value > 32767L)
				raiseOverflowException(number, targetClass);
			return new Short(number.shortValue());
		}
		if (targetClass.equals(java.lang.Integer.class)) {
			long value = number.longValue();
			if (value < -2147483648L || value > 2147483647L)
				raiseOverflowException(number, targetClass);
			return new Integer(number.intValue());
		}
		if (targetClass.equals(java.lang.Long.class))
			return new Long(number.longValue());
		if (targetClass.equals(java.math.BigInteger.class))
			if (number instanceof BigDecimal)
				return ((BigDecimal) number).toBigInteger();
			else
				return BigInteger.valueOf(number.longValue());
		if (targetClass.equals(java.lang.Float.class))
			return new Float(number.floatValue());
		if (targetClass.equals(java.lang.Double.class))
			return new Double(number.doubleValue());
		if (targetClass.equals(java.math.BigDecimal.class))
			return new BigDecimal(number.toString());
		else
			throw new IllegalArgumentException("Could not convert number [" + number + "] of type ["
					+ number.getClass().getName() + "] to unknown target class [" + targetClass.getName() + "]");
	}

	private static void raiseOverflowException(Number number, Class targetClass) {
		throw new IllegalArgumentException("Could not convert number [" + number + "] of type ["
				+ number.getClass().getName() + "] to target class [" + targetClass.getName() + "]: overflow");
	}

	public static Number parseNumber(String text, Class targetClass) {
		Assert.notNull(text, "Text must not be null");
		Assert.notNull(targetClass, "Target class must not be null");
		String trimmed = StringUtils.trimAllWhitespace(text);
		if (targetClass.equals(java.lang.Byte.class))
			return isHexNumber(trimmed) ? Byte.decode(trimmed) : Byte.valueOf(trimmed);
		if (targetClass.equals(java.lang.Short.class))
			return isHexNumber(trimmed) ? Short.decode(trimmed) : Short.valueOf(trimmed);
		if (targetClass.equals(java.lang.Integer.class))
			return isHexNumber(trimmed) ? Integer.decode(trimmed) : Integer.valueOf(trimmed);
		if (targetClass.equals(java.lang.Long.class))
			return isHexNumber(trimmed) ? Long.decode(trimmed) : Long.valueOf(trimmed);
		if (targetClass.equals(java.math.BigInteger.class))
			return isHexNumber(trimmed) ? decodeBigInteger(trimmed) : new BigInteger(trimmed);
		if (targetClass.equals(java.lang.Float.class))
			return Float.valueOf(trimmed);
		if (targetClass.equals(java.lang.Double.class))
			return Double.valueOf(trimmed);
		if (targetClass.equals(java.math.BigDecimal.class) || targetClass.equals(java.lang.Number.class))
			return new BigDecimal(trimmed);
		else
			throw new IllegalArgumentException("Cannot convert String [" + text + "] to target class ["
					+ targetClass.getName() + "]");
	}

	public static Number parseNumber(String text, Class targetClass, NumberFormat numberFormat) {
		if (numberFormat != null) {
			Assert.notNull(text, "Text must not be null");
			Assert.notNull(targetClass, "Target class must not be null");
			DecimalFormat decimalFormat = null;
			boolean resetBigDecimal = false;
			if (numberFormat instanceof DecimalFormat) {
				decimalFormat = (DecimalFormat) numberFormat;
				if ((java.math.BigDecimal.class).equals(targetClass)
						&& (decimalFormatSupportsBigDecimal && !decimalFormat.isParseBigDecimal())) {
					decimalFormat.setParseBigDecimal(true);
					resetBigDecimal = true;
				}
			}
			try {
				Number number1;
				try {
					Number number = numberFormat.parse(StringUtils.trimAllWhitespace(text));
					number1 = convertNumberToTargetClass(number, targetClass);
				} catch (ParseException ex) {
					IllegalArgumentException iae = new IllegalArgumentException("Could not parse number: "
							+ ex.getMessage());
					iae.initCause(ex);
					throw iae;
				}
				return number1;
			} finally {
				if (resetBigDecimal)
					decimalFormat.setParseBigDecimal(false);
			}
		} else {
			return parseNumber(text, targetClass);
		}
	}

	private static boolean isHexNumber(String value) {
		int index = value.startsWith("-") ? 1 : 0;
		return value.startsWith("0x", index) || value.startsWith("0X", index) || value.startsWith("#", index);
	}

	private static BigInteger decodeBigInteger(String value) {
		int radix = 10;
		int index = 0;
		boolean negative = false;
		if (value.startsWith("-")) {
			negative = true;
			index++;
		}
		if (value.startsWith("0x", index) || value.startsWith("0X", index)) {
			index += 2;
			radix = 16;
		} else if (value.startsWith("#", index)) {
			index++;
			radix = 16;
		} else if (value.startsWith("0", index) && value.length() > 1 + index) {
			index++;
			radix = 8;
		}
		BigInteger result = new BigInteger(value.substring(index), radix);
		return negative ? result.negate() : result;
	}

	/**
	* 将 {@code java.math.BigDecimal} 的字符串表示形式转换为
	* {@code java.math.BigDecimal}。 该方法相当于
	* <code>{@linkplain #parseDecimal(String, BigDecimal) parseDecimal(str, new BigDecimal("0.0"))}</code>。
	*
	* @param str {@code java.math.BigDecimal} 的字符串表示形式。
	* @return 用字符串参数表示的 {@code java.math.BigDecimal}。
	*/
	public static BigDecimal parseDecimal(String str) {
		return parseDecimal(str, new BigDecimal("0.0"));
	}

	/**
	 * 将 {@code java.math.BigDecimal} 的字符串表示形式转换为
	 * {@code java.math.BigDecimal}。
	 *
	 * @param str {@code java.math.BigDecimal} 的字符串表示形式。
	 * @param defaultValue 解析错误时，返回的默认值。
	 * @return 用字符串参数表示的 {@code java.math.BigDecimal}。
	 */
	public static BigDecimal parseDecimal(String str, BigDecimal defaultValue) {
		if (str == null || "".equals(str.trim()))
			return defaultValue;
		try {
			return new BigDecimal(str);
		} catch (NumberFormatException ex) {
			return defaultValue;
		}
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code byte}。该方法相当于
	 * {@code {@linkplain #parseByte(String, byte) parseByte(str, (byte)0)}}。
	 *
	 * @param str 要解析的字符串。
	 * @return 参数（十进制）表示的 {@code byte} 值。
	 */
	public static byte parseByte(String str) {
		return parseByte(str, (byte) 0);
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code byte}。该字符串中的字符必须都是十进制数字，除非第一个字符是表示负值的
	 * ASCII 符号中的负号 '-' ('\u002D')。如果解析出的值超出 {@code java.lang.Byte}
	 * 的值域范围，或者字符串参数解析错误，则返回给定的默认值 {@code defaultValue}。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code java.lang.Byte} 值域范围时，返回的默认值。
	 * @return 参数（十进制）表示的 {@code byte} 值。
	 */
	public static byte parseByte(String str, byte defaultValue) {
		if (str == null || "".equals(str.trim()))
			return defaultValue;
		try {
			return Byte.parseByte(str);
		} catch (NumberFormatException ex) {
			return defaultValue;
		}
	}

	/**
	 * 返回一个保持指定 {@code java.lang.String} 所给出的值的
	 * {@code java.lang.Byte} 对象。该方法相当于
	 * <code>Byte.valueOf({@linkplain #parseByte(String, byte) parseByte(str, (byte)defaultValue)})</code>。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code >java.lang.Byte} 值域范围时，返回的默认值。
	 * @return 保持字符串参数所表示的值的 {@code java.lang.Byte} 对象
	 */
	public static Byte parseByte(String str, Byte defaultValue) {
		return Byte.valueOf(parseByte(str, defaultValue.byteValue()));
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code short}。该方法相当于
	 * <code>{@linkplain #parseShort(String, short) parseShort(str, (short)0)}</code>。
	 *
	 * @param str 包含要解析的 {@code short} 表示形式的
	 *            {@code java.lang.String}。
	 * @return 参数（十进制）表示的 {@code short} 值。
	 */
	public static short parseShort(String str) {
		return parseShort(str, (short) 0);
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code short}。该字符串中的字符必须都是十进制数字，除非第一个字符是表示负值的
	 * ASCII 符号中的负号 '-' ('\u002D')。如果解析出的值超出 {@code java.lang.Short}
	 * 的值域范围，或者字符串参数解析错误，则返回给定的默认值 {@code defaultValue}。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code java.lang.Short} 值域范围时，返回的默认值。
	 * @return 参数（十进制）表示的 {@code short} 值。
	 */
	public static short parseShort(String str, short defaultValue) {
		if (str == null || "".equals(str.trim()))
			return defaultValue;
		try {
			return Short.parseShort(str);
		} catch (NumberFormatException ex) {
			return defaultValue;
		}
	}

	/**
	 * 返回一个保持指定 {@code java.lang.String} 所给出的值的
	 * {@code java.lang.Short} 对象。 该方法相当于
	 * <code>Short.valuOf({@linkplain #parseShort(String, short) parseShort(str, (short)defaultValue)})</code>。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code >java.lang.Short} 值域范围时，返回的默认值。
	 * @return 保持字符串参数所表示的值的 {@code java.lang.Short} 对象
	 */
	public static Short parseShort(String str, Short defaultValue) {
		return Short.valueOf(parseShort(str, defaultValue.shortValue()));
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code int}。该方法相当于
	 * <code>{@linkplain #parseInt(String, int) parseInt(str, 0)}</code>。
	 *
	 * @param str 要解析的字符串。
	 * @return 参数（十进制）表示的 {@code int} 值。
	 */
	public static int parseInt(String str) {
		return parseInt(str, 0);
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code int}。该字符串中的字符必须都是十进制数字，除非第一个字符是表示负值的
	 * ASCII 符号中的负号 '-' ('\u002D')。如果解析出的值超出 {@code java.lang.Integer}
	 * 的值域范围，或者字符串参数解析错误，则返回给定的默认值 {@code defaultValue}。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code java.lang.Integer}
	 *            值域范围时，返回的默认值。
	 * @return 参数（十进制）表示的 {@code int} 值。
	 */
	public static int parseInt(String str, int defaultValue) {
		if (str == null || "".equals(str.trim()))
			return defaultValue;
		try {
			return Integer.parseInt(str);
		} catch (NumberFormatException ex) {
			return defaultValue;
		}
	}

	/**
	 * 返回一个保持指定 {@code java.lang.String} 所给出的值的
	 * {@code java.lang.Integer} 对象。 该方法相当于
	 * <code>Integer.valueOf({@linkplain #parseInt(String, int) parseInt(str, 0)})</code>。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code Integer} 值域范围时，返回的默认值。
	 * @return 保持字符串参数所表示的值的 {@code java.lang.Integer} 对象
	 */
	public static Integer parseInt(String str, Integer defaultValue) {
		return Integer.valueOf(parseInt(str, defaultValue.intValue()));
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code long}。该方法相当于
	 * <code>{@linkplain #parseLong(String, long) parseLong(str, 0L)}</code>。
	 *
	 * @param str 要解析的字符串。
	 * @return 参数（十进制）表示的 {@code long} 值。
	 */
	public static long parseLong(String str) {
		return parseLong(str, 0L);
	}

	/**
	 * 将字符串参数解析为有符号的十进制 {@code long}。该字符串中的字符必须都是十进制数字，除非第一个字符是表示负值的
	 * ASCII 符号中的负号 '-' ('\u002D')，它表示一个负值。如果解析出的值超出 {@code java.lang.Long}
	 * 值域范围，或者字符串参数解析错误，则返回给定的默认值 {@code defaultValue}。
	 * <p>
	 * 注意，不允许将字符 L ('\u004C') 和 l ('\u006C') 作为类型指示符出现在字符串的结尾处，这一点在 Java
	 * 编程语言源代码中是允许的。
	 * </p>
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code java.lang.Long} 值域范围时，返回的默认值。
	 * @return 参数（十进制）表示的 {@code long} 值。
	 */
	public static long parseLong(String str, long defaultValue) {
		if (str == null || "".equals(str.trim()))
			return defaultValue;
		try {
			return Long.parseLong(str);
		} catch (NumberFormatException ex) {
			return defaultValue;
		}
	}

	/**
	 * 返回一个保持指定 {@code java.lang.String} 所给出的值的{@code java.lang.Long}
	 * 对象。 该方法相当于
	 * <code>Long.valueOf({@linkplain #parseLong(String, long) parseLong(str, (long)defaultValue)})</code>。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code java.lang.Long} 值域范围时，返回的默认值。
	 * @return 保持字符串参数所表示的值的 {@code java.lang.Long} 对象
	 */
	public static Long parseLong(String str, Long defaultValue) {
		return Long.valueOf(parseLong(str, defaultValue.longValue()));
	}

	/**
	 * 返回一个新的 {@code float} 值，该值被初始化为用指定 {@code java.lang.String}
	 * 表示的值。该方法相当于
	 * <code>{@linkplain #parseFloat(String, float) parseFloat(str, 0.0f)}</code>。
	 *
	 * @param str 要解析的字符串。
	 * @return 用字符串参数表示的 {@code float} 值。
	 */
	public static float parseFloat(String str) {
		return parseFloat(str, 0.0f);
	}

	/**
	 * 返回一个新的 {@code float} 值，该值被初始化为用指定 {@code java.lang.String}
	 * 表示的值。如果解析出的值超过{@code java.lang.Float} 值域范围，或者字符串参数解析错误，则返回给定的默认值
	 * {@code defaultValue}。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code java.lang.Float} 值域范围时，返回的默认值。
	 * @return 用字符串参数表示的 {@code float} 值。
	 */
	public static float parseFloat(String str, float defaultValue) {
		if (str == null || "".equals(str.trim()))
			return defaultValue;
		try {
			return Float.parseFloat(str);
		} catch (NumberFormatException ex) {
			return defaultValue;
		}
	}

	/**
	 * 返回一个新的 {@code double} 值，该值被初始化为用指定 {@code java.lang.String}
	 * 表示的值。该方法相当于
	 * <code>{@linkplain #parseDouble(String, double) parseDouble(str, 0.0d)}</code>。
	 *
	 * @param str 要解析的字符串。
	 * @return 用字符串参数表示的 {@code double} 值。
	 */
	public static double parseDouble(String str) {
		return parseDouble(str, 0.0d);
	}

	/**
	 * 返回一个新的 {@code double} 值，该值被初始化为用指定 {@code java.lang.String}
	 * 表示的值。如果解析出的值超过{@code java.lang.Double} 值域范围，或者字符串参数解析错误，则返回给定的默认值
	 * {@code defaultValue}。
	 *
	 * @param str 要解析的字符串。
	 * @param defaultValue 解析错误或超出 {@code java.lang.Double} 值域范围时，返回的默认值。
	 * @return 用字符串参数表示的 {@code double} 值。
	 */
	public static double parseDouble(String str, double defaultValue) {
		if (str == null || "".equals(str.trim()))
			return defaultValue;
		try {
			return Double.parseDouble(str);
		} catch (NumberFormatException ex) {
			return defaultValue;
		}
	}

	private static final boolean decimalFormatSupportsBigDecimal;

	static {
		decimalFormatSupportsBigDecimal = ClassUtils.hasMethod(java.text.DecimalFormat.class, "setParseBigDecimal",
				new Class[] { Boolean.TYPE });
	}
	/**
	 * 判断是否全为数字
	 * @param strin
	 * @return
	 */
	public boolean isNumeric(String str)
	{
	       Pattern pattern = Pattern.compile("[0-9]*");
	       Matcher isNum = pattern.matcher(str);
	       return isNum.matches();
	}


}
